<!-- // version 2020/05/19 -->
<!-- do not using script src, since the onBoard server can not operate at teh same time -->
<!-- pic in base64 url -->
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1.0">
    <title>designRuler</title>
    <style>
        * {
            font-family: Arial, 'Microsoft Yahei', Helvetica, sans-serif, 'Segoe UI', 'Sans';
            -ms-overflow-style: none;
            scrollbar-width: none;
        }

        *::-webkit-scrollbar {
            display: none;
        }
    </style>
</head>

<body style="background-color: #f4f2f4; margin: 0;">
    <div class="navbar" style="top:0;left:0;right:0;position:fixed;"></div>
    <div style="min-height: 48px;"></div>
    <!------------------------- start Content ----------------------------->
    <div class="rulerPage" style="padding:8px;"></div>
    <div class="picPage" style="padding:8px;"></div>
    <script> // navBar.js
        function navBar(navbarBox, opts) {
            // parse parameters ===================//
            if (typeof navbarBox == "string") navbarBox = document.querySelector(navbarBox);
            if (typeof opts == "undefined") opts = {};
            var title = ("title" in opts) ? opts.title : '';
            var titleColor = ("titleColor" in opts) ? 'color:' + opts.titleColor + ';' : 'color:#233;';
            var minItemWidth = ("minItemWidth" in opts) ? 'min-width:' + opts.minItemWidth + ';' : '';
            var navHeadImg = ("imageSrc" in opts) ? '<img src="' + opts.imageSrc
                + '" style="border-radius:6px;width:1.2em;height:1.2em;padding:0 0.2em">' : '';

            // init html and css ===================//
            { // navbarStyle
                var navbarStyle = '\
            __ID__ .navbar {background-color:#ffffff; box-shadow:6px 0px 6px #ddd;}                     \
            __ID__ .navHead {float:left;text-align:center;font-size:30px;padding:8px 24px;}             \
            __ID__ .navMenu {display: none; float: left; cursor: pointer; }                             \
            __ID__ .navTitle {margin:auto;font-weight:700;display:inline-block;text-decoration:none;}   \
            __ID__ .navList {display: block; text-align: right; padding: 8px; }                         \
            __ID__ .navList .item {cursor:pointer;display:inline-block;text-decoration:none;            \
                padding: 8px;font-size:16px;font-weight:600;text-align:center; }                        \
            __ID__ .navList .item:hover {background-color:#aaa; color:#fff !important;}                 \
            __ID__ .navList.sub { display: none; background: #f9f9f9; }                                 \
            @media screen and (max-width: 600px) {                                                      \
            __ID__ .navHead {float:none;}  __ID__ .navMenu {display:inline-block;}                      \
            __ID__ .navList {float:none;display:none;text-align:left;border-top:2px inset #dddddd;}     \
            __ID__ .navList .item {display:block;text-align:left;}}';
            }
            { //navbarHtml
                var menuIcon = '<div style="padding:0.15em 0.1em;box-sizing:content-box;">'
                    + '<div style="height:0.14em;width:0.8em;box-sizing:content-box;'
                    + 'border-top:0.14em solid #555;"></div>'
                    + '<div style="height:0.14em;width:0.8em;box-sizing:content-box;'
                    + 'border-bottom:0.14em solid #555;border-top:0.14em solid #555;"></div></div>';
                var navbarHtml = '<div class="navbar">'
                    + '<div class="navHead"><a class="navMenu">' + menuIcon + '</a><a class="navTitle"'
                    + 'href="#" style="' + titleColor + '">' + navHeadImg + title + '</a></div>'
                    + '<div class="mainList"></div><div class="subList"></div></div>';
            }
            function insertStyleHtml(styleText, htmlText, container, uniqueIDSign) {
                if (typeof uniqueIDSign == "undefined") uniqueIDSign = "__ID__";
                var uniqueID = "";
                while (1) {
                    uniqueID = ".ID" + Math.floor((new Date().getTime() * Math.random()));
                    if (document.querySelector(uniqueID) == null) break;
                }
                scopedStyle = styleText.replace(new RegExp(uniqueIDSign, "g"), uniqueID);
                if (scopedStyle.indexOf('<style') == -1) scopedStyle = '<style>' + scopedStyle + '</style>';
                container.className += " " + uniqueID.slice(1), container.innerHTML = scopedStyle + htmlText;
            }
            insertStyleHtml(navbarStyle, navbarHtml, navbarBox);

            // local vars =========================//
            var navbar = navbarBox.querySelector('.navbar');
            var navMenu = navbar.querySelector('.navMenu');
            var mainList = navbar.querySelector('.mainList');
            var subList = navbar.querySelector('.subList');

            // additional Events and Actions ======//
            navMenu.onclick = function () { hideNavList('.navList.main'); hideNavList('.navList.sub', true); };

            // local functions ====================//
            function hideNavList(Selector, hide) {

                function hideListCore(navList, hide) {
                    if (hide == "rev") navList.style.display = navList.style.display ? "" : "block";
                    else if (hide) navList.style.display = "";
                    else navList.style.display = "block";
                }

                if (typeof hide == "undefined") hide = "rev";
                var mainLists = mainList.querySelectorAll(Selector);
                var subLists = subList.querySelectorAll(Selector);
                var mainLength = mainLists.length, subLength = subLists.length;
                if (mainLength)
                    for (var i = 0; i < mainLength; i++) hideListCore(mainLists[i], hide);
                if (subLength)
                    for (var i = 0; i < subLength; i++) hideListCore(subLists[i], hide);
            }

            function genNavlist(isSub, members, color, name) {
                // members: [{text,idName,className,clickAct,minWidth}]
                if (typeof members == "undefined") members = [{}];
                var memberLength = members.length, memberList = [];
                var name = (typeof name == "undefined") ? '' : name;
                var color = (typeof color == "undefined") ? '' : 'color:' + color + ';';

                for (var i = 0; i < memberLength; i++) {
                    var mem = members[i], text, idName, className, inMinWidth;
                    text = ("text" in mem) ? mem.text : "";
                    idName = ("idName" in mem) ? mem.idName : "";
                    className = ("className" in mem) ? mem.className : "";
                    clickAct = ("clickAct" in mem) ? 'onclick="' + mem.clickAct + '";' : '';
                    inMinWidth = ("minWidth" in mem) ? 'min-width:' + mem.minWidth + ';"' : minItemWidth;
                    memberList.push('<a class="item ' + className + '" id="' + idName + '" '
                        + clickAct + '" style="' + color + inMinWidth + '" >' + text + '</a>');
                }

                if (isSub)
                    return '<div class="navList sub ' + name + '">\n' + memberList.join('\n') + '\n</div>';
                else
                    return '<div class="navList main">\n' + memberList.join('\n') + '\n</div>';
            }

            // methods ============================//
            this.hideNavList = hideNavList;
            this.queryItem = function (Selector) {
                var item = mainList.querySelector(Selector);
                if (!item) item = subList.querySelector(Selector);
                return item;
            }
            this.appendNavList = function (isSub, members, color, name) {
                // members: [{text,idName,className,minWidth}]
                if (isSub) subList.innerHTML += genNavlist(true, members, color, name);
                else mainList.innerHTML += genNavlist(false, members, color, name);
            }
        }

    </script>
    <script> // rulerPage.js
        function sliderBar(sliderBox, opts) {
            // parse parameters =============================//
            if (typeof sliderBox == "string") sliderBox = document.querySelector(sliderBox);
            if (typeof opts == "undefined") opts = {};
            var sliderColor = ("sliderColor" in opts) ? opts.sliderColor : "#ddd";
            var bufferColor = ("bufferColor" in opts) ? opts.bufferColor : "#aaa";
            var progressColor = ("progressColor" in opts) ? opts.progressColor : "#2d3";

            // init html and css ===================================//
            sliderBox.innerHTML = '\
        <div class="backdrop" style="overflow:hidden;width:100%;">              \
        <div class="contain" style="padding:8px 0;margin:0 8px;cursor:pointer;">\
        <div class="slider" style="height:3px;position:relative;                \
        border-radius:1px;background:' + sliderColor + ';">                     \
        <div class="buffer" style="height:100%;width:0%;position:absolute;      \
        border-radius:1px;background:' + bufferColor + ';"></div>               \
        <div class="prog" style="height:100%;width:0%;position:absolute;        \
        border-radius:1px;background:' + progressColor + ';"></div>             \
        <div class="point" style="height:9px;width:9px;position:absolute;       \
        transform:translate(-5px,-3px);left:0%;border-radius:50%;               \
        background:' + progressColor + ';"></div></div></div></div>             ';

            // methods ===================================//
            this.setClickCallBack = function (callback) {
                var contain = sliderBox.querySelector('.contain');
                contain.onclick = function (evt) {
                    callback(evt.offsetX / contain.offsetWidth);
                }
            }
            this.updateCurrent = function (current) {
                // current:  support 0.6 60 60%
                var tmp = Math.abs(100 * parseFloat(current));
                tmp = (tmp <= 100) ? (tmp) + "%" : (tmp / 100) + "%";
                sliderBox.querySelector('.prog').style.width = tmp;
                sliderBox.querySelector('.point').style.left = tmp;
            }
            this.updateBuffer = function (buffer) {
                // buffer:  support 0.6 60 60%
                var tmp = Math.abs(100 * parseFloat(buffer));
                tmp = (tmp <= 100) ? (tmp) + "%" : (tmp / 100) + "%";
                sliderBox.querySelector('.buffer').style.width = tmp;
            }
        }
        function rulerPage(rulerBox, opts) {
            // parse parameters ================//
            if (typeof rulerBox == "string") rulerBox = document.querySelector(rulerBox);
            if (typeof opts == "undefined") opts = {};
            var maxLength = ("maxLength" in opts) ? opts.maxLength : 32767;
            var backImage = ("backImage" in opts) ? opts.backImage : '';
            var changeWidth = ("changeWidth" in opts) ? opts.changeWidth : 600;
            var arrayStreamCallBack = ("arrayStreamCallBack" in opts) ? opts.arrayStreamCallBack : function () { };

            // init html and css ===============//
            { // rulerStyle
                var rulerStyle = '\
        __ID__ .leftcol {float: left; width: 21%;} __ID__ .rightcol {overflow: hidden;}\
        __ID__ .card {padding:12px;margin:12px 8px;overflow:hidden;\
            border-radius:6px;background-color:#fff; }\
        __ID__ .center {display:flex; flex-wrap:wrap; align-items:center; justify-content:center;}\
        __ID__ .card .title {font-size:20px;font-weight:600;;text-align:left; }\
        __ID__ .card .item {font-size:14px;font-weight:600;text-align:center;color:#666; }\
        __ID__ .card .info {font-size:16px;font-weight:600;text-align:center;overflow:auto;}\
        @media screen and (max-width: '+ changeWidth + 'px) {\
            __ID__ .leftcol, __ID__ .rightcol{float:none;width:100%;padding: 0;}}';
            }
            { // rulerHtml
                var rulerHtml = '<div class="leftcol" style="font-weight:600;">\
        <div class="card"><div class="title">Paras</div>\
        <div class="card rRate" style="background:#fde;">\
        <div class="item">radius / Radius</div>\
        <div style="display:flex;align-items:center;">\
        <div class="range" style="flex:auto;"></div>\
        <div class="info" style="padding:0 10px;">0.00</div></div></div>\
        <div class="card dRate" style="background:#ffe;">\
        <div class="item">distance / Radius</div>\
        <div style="display:flex;align-items:center;">\
        <div class="range" style="flex:auto;"></div>\
        <div class="info" style="padding:0 10px;">0.00</div></div></div>\
        </div><div class="card" style="text-align:center;" >\
        <div class="card paint" style="cursor:pointer;background:#ddf;">paint</div>\
        </div></div>\
        <div class="rightcol"><div class="card">\
        <div class="title">Preview</div>\
        <div class="card center"><canvas></canvas></div></div></div>';
            }
            function insertStyleHtml(styleText, htmlText, container, uniqueIDSign) {
                if (typeof uniqueIDSign == "undefined") uniqueIDSign = "__ID__";
                var uniqueID = "";
                while (1) {
                    uniqueID = ".ID" + Math.floor((new Date().getTime() * Math.random()));
                    if (document.querySelector(uniqueID) == null) break;
                }
                scopedStyle = styleText.replace(new RegExp(uniqueIDSign, "g"), uniqueID);
                if (scopedStyle.indexOf('<style') == -1) scopedStyle = '<style>' + scopedStyle + '</style>';
                container.className += " " + uniqueID.slice(1), container.innerHTML = scopedStyle + htmlText;
            }
            insertStyleHtml(rulerStyle, rulerHtml, rulerBox);

            // local vars =================//
            var timeTik, startLength = 0; // for updateImage:setTimeout 
            var rRate = 0.58, dRate = 0.32, R, r, d;
            var canvas = rulerBox.querySelector('canvas');
            var ctx = canvas.getContext('2d');
            var imageData, rulerArrayStream;
            var elrRate = rulerBox.querySelector('.rRate');
            var eldRate = rulerBox.querySelector('.dRate');
            var elpaint = rulerBox.querySelector('.paint');
            var rRateText = elrRate.querySelector('.info');
            var dRateText = eldRate.querySelector('.info');
            var rRateRange = new sliderBar(elrRate.querySelector('.range'));
            var dRateRange = new sliderBar(eldRate.querySelector('.range'));

            // addtional Events and Actions ==================//
            window.onresize = function () { preCanvas(makeRuler); };
            elpaint.onclick = function () { makeRuler(); arrayStreamCallBack(rulerArrayStream); }
            rRateRange.updateCurrent(rRate), rRateText.innerText = rRate.toFixed(2);
            dRateRange.updateCurrent(dRate), dRateText.innerText = dRate.toFixed(2);
            rRateRange.setClickCallBack(function (rate) {
                rRate = rate;
                if (rRate > 1) rRate = 1;
                if (rRate < 0) rRate = 0;
                rRateRange.updateCurrent(rRate), rRateText.innerText = rRate.toFixed(2);
            });
            dRateRange.setClickCallBack(function (rate) {
                dRate = rate;
                if (dRate > 1) dRate = 1;
                if (dRate < 0) dRate = 0;
                dRateRange.updateCurrent(dRate), dRateText.innerText = dRate.toFixed(2);
            });

            // local functions =================//
            function preCanvas(makeRuler) {
                var width = window.innerWidth;
                var height = window.innerHeight;
                height = height * 0.6;
                width = (width > changeWidth) ? width * 0.5 : width * 0.75;
                if (height > width) height = width;
                else width = height;
                canvas.height = height, canvas.width = width;
                var colorImage = new Image(width, height);
                colorImage.src = backImage;
                colorImage.onload = function () {
                    ctx.drawImage(colorImage, 0, 0, width, height);
                    imageData = ctx.getImageData(0, 0, width, height);
                    makeRuler();
                }
            }
            function getRulerPara() {
                rRate = Number(rRate), dRate = Number(dRate);
                var size = Math.min(canvas.width, canvas.height);
                var total = 1 - rRate + dRate + 0.1; // 0.1 is extra border
                R = Math.floor(size / (2 * total));
                r = Math.floor(R * rRate);
                d = Math.floor(R * dRate);
            }
            function calcRuler(R, r, d, rulerStart, rulerEnd, density) {
                function getXY(R, r, d, t) {
                    return {
                        x: Math.round((R - r) * Math.cos(t) + d * Math.cos(t * (R - r) / r)),
                        y: Math.round((R - r) * Math.sin(t) - d * Math.sin(t * (R - r) / r))
                    };
                }
                var ruler = [], w = density * ((R - r) / r);
                for (var t = rulerStart; t < rulerEnd; t++) {
                    ruler.push(getXY(R, r, d, t / w));
                }
                return ruler;
            }
            function drawRuler(imageData, coordinate) {
                var width = imageData.width, height = imageData.height;
                var size = width * height;
                var length = coordinate.length;
                for (var i = 0; i < length; i++) {
                    var w = Math.round(width / 2 + coordinate[i].x);
                    var h = Math.round(height / 2 + coordinate[i].y);
                    var pos = []; // neighbors
                    pos[0] = (w + h * width) * 4; // center
                    pos[1] = pos[0] - 4; // left
                    pos[2] = pos[0] + 4; // right
                    pos[3] = pos[0] - width * 4; // up
                    pos[4] = pos[0] + width * 4; // bottom
                    pos[5] = pos[3] - 4; // upLeft
                    pos[6] = pos[3] + 4; // upRight
                    pos[7] = pos[4] - 4; // bottomLeft
                    pos[8] = pos[4] + 4; // bottomRight
                    var maxPos = size * 4;
                    for (var j = 0; j < 9; j++) {
                        if (pos[j] < 0 || pos[j] > maxPos) pos[j] = pos[0];
                        imageData.data[pos[j] + 3] = 255;
                    }
                }
                return imageData;
            }
            function hideImage(imageData) {
                var size = imageData.width * imageData.height;
                for (var i = 0; i < size; i++) {
                    imageData.data[(i << 2) + 3] = 0;
                }
                return imageData;
            }
            function updateImage() {
                if (startLength > maxLength) return;
                try { clearTimeout(timeTik); } catch (err) { }
                var endLength = startLength + 64;
                var ruler = calcRuler(R, r, d, startLength, endLength, 128);
                imageData = drawRuler(imageData, ruler);
                ctx.putImageData(imageData, 0, 0);
                startLength = endLength;
                timeTik = setTimeout(function () { updateImage(); }, 40);
            }
            function makeRuler() {
                imageData = hideImage(imageData);
                ctx.putImageData(imageData, 0, 0);
                getRulerPara();
                rulerArrayStream = formatRulerPara(rRate, dRate);
                try { clearTimeout(timeTik); } catch (err) { }
                startLength = 0;
                updateImage();
            }
            function formatRulerPara(rRate, dRate) {
                var rulerArrayStream = new Uint8Array(12);
                // rulerArrayStream: [uint8_t:[PARA], uint8_t:rRate, uint8_t:dRate] 
                // (uint8_t) rulerArrayStream: ['P','A','R','A', rRate*256, dRate*256]
                rulerArrayStream[0] = 'P'.charCodeAt();
                rulerArrayStream[1] = 'A'.charCodeAt();
                rulerArrayStream[2] = 'R'.charCodeAt();
                rulerArrayStream[3] = 'A'.charCodeAt();
                rulerArrayStream[4] = Math.round(rRate * 256);
                rulerArrayStream[5] = Math.round(dRate * 256);
                rulerArrayStream[6] = '\r'.charCodeAt();
                rulerArrayStream[7] = '\n'.charCodeAt();
                rulerArrayStream[8] = 'E'.charCodeAt();
                rulerArrayStream[9] = 'N'.charCodeAt();
                rulerArrayStream[10] = 'D'.charCodeAt();
                rulerArrayStream[11] = '\0'.charCodeAt();
                return rulerArrayStream;
            }

            // methods =========================//
            this.refresh = function () { preCanvas(makeRuler); };
        }
    </script>
    <script>
        // local vars =================================//
        var nvb = new navBar(document.querySelector('.navbar'), {
            title: "designRuler", titleColor: "#cae", minItemWidth: "80px",
        });
        var ruler = new rulerPage(document.querySelector('.rulerPage'), {
            changeWidth: 600, arrayStreamCallBack: function (arrayStream) {
                sendStream(arrayStream);
            }
        });

        // addtional Events and Actions =============================//
        nvb.appendNavList(false, [
            { text: "uploadPic", clickAct: "window.open('./picPage.html', '_self');" },
            { text: "designRuler", clickAct: "" },
        ], "#eac");
        document.querySelector('.rulerPage').style.display = "";
        document.querySelector('.picPage').style.display = "none";
        ruler.refresh();

        function sendStream(arrayStream) {
            var xhr = new XMLHttpRequest();
            xhr.open("POST", "/", true);
            xhr.setRequestHeader("Content-Type", "application/octet-stream");
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
                    console.log(arrayStream);
                }
            };
            xhr.send(arrayStream);
        }
    </script>
</body>

</html>